﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Web.Http;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Style.eCommerce.API.Controllers;
using Style.eCommerce.API.Models;
using Moq;
using Style.eCommerce.API.Repositories;
using Style.eCommerce.API.APIReferences.ComercioLibre;

namespace Style.eCommerce.API.Tests.Controllers
{
    [TestClass]
    public class StoreControllerTest
    {
        public Mock<IRepository<Store>> MockStoreRepository { get; set; }
        public Mock<IRepository<Product>> MockProductRepository { get; set; }
        public Mock<IComercioLibreClient> MockComercioLibreClient { get; set; }

        [TestInitialize()]
        public void Initialize() 
        {
            MockStoreRepository = new Mock<IRepository<Store>>();
            MockProductRepository = new Mock<IRepository<Product>>();
            MockComercioLibreClient = new Mock<IComercioLibreClient>();
        }

        [TestMethod]
        public void GetAllStores_ExpectedNonStore()
        {
            // Arrange
            MockStoreRepository.Setup(l => l.GetAll())
                .Returns(new Store[] {});

            var controller = GetController();

            // Act
            var response = controller.GetAllStores();

            // Assert
            IEnumerable<Store> stores;
            Assert.IsTrue(response.TryGetContentValue<IEnumerable<Store>>(out stores));
            Assert.IsNotNull(stores);
            Assert.AreEqual(0, stores.Count());
        }

        [TestMethod]
        public void GetAllStores_ExpectedAnyStore()
        {
            // Arrange
            MockStoreRepository.Setup(l => l.GetAll())
                .Returns(new Store[]
            {
                new Store() { Id = 34, Name = "Apple", ComercioLibreId = 230 },
                new Store() { Id = 39, Name = "Nike", ComercioLibreId = 534 }
            });

            var controller = GetController();

            // Act
            var response = controller.GetAllStores();

            // Assert
            IEnumerable<Store> stores;
            Assert.IsTrue(response.TryGetContentValue<IEnumerable<Store>>(out stores));
            Assert.IsNotNull(stores);
            Assert.AreEqual(2, stores.Count());
            Assert.AreEqual("Apple", stores.ElementAt(0).Name);
            Assert.AreEqual("Nike", stores.ElementAt(1).Name);
        }

        [TestMethod]
        public void GetProductsFromStore_WithoutProducts()
        {
            // Arrange
            MockStoreRepository.Setup(l => l.GetById(34))
                .Returns(new Store() { Id = 34, Name = "Apple", ComercioLibreId = 230 });

            MockComercioLibreClient.Setup(l => l.GetSellerProducts(230))
                .Returns(new CLProductsList()
                {
                    Total = 0,
                    Products = new CLProductSummary[] { }
                });

            var controller = GetController();
            var storeId = 34;

            // Act
            var response = controller.GetProducts(storeId);

            // Assert
            IEnumerable<ProductSummary> products;
            Assert.IsTrue(response.TryGetContentValue<IEnumerable<ProductSummary>>(out products));
            Assert.IsNotNull(products);
            Assert.AreEqual(0, products.Count());
        }

        [TestMethod]
        public void GetProductsFromStore_WithAnyProducts()
        {
            // Arrange
            MockStoreRepository.Setup(l => l.GetById(34))
                .Returns(new Store() { Id = 34, Name = "Apple", ComercioLibreId = 230 });

            MockComercioLibreClient.Setup(l => l.GetSellerProducts(230))
                .Returns(new CLProductsList()
                {
                    Total = 2,
                    Products = new CLProductSummary[]
                    {
                        new CLProductSummary()
                        {
                            Title = "iPhone 6"
                        },
                        new CLProductSummary()
                        {
                            Title = "Watch"
                        }
                    }
                });

            var controller = GetController();
            var storeId = 34;

            // Act
            var response = controller.GetProducts(storeId);

            // Assert
            IEnumerable<ProductSummary> products;
            Assert.IsTrue(response.TryGetContentValue<IEnumerable<ProductSummary>>(out products));
            Assert.IsNotNull(products);
            Assert.AreEqual(2, products.Count());
            Assert.AreEqual("iPhone 6", products.ElementAt(0).Title);
            Assert.AreEqual("Watch", products.ElementAt(1).Title);
        }

        [TestMethod]
        public void GetProductsFromNonexistentStore()
        {
            // Arrange
            MockStoreRepository.Setup(l => l.GetById(34)).Throws(new ArgumentException("Invalid Store"));
            
            var controller = GetController();
            var storeId = 34;

            // Act
            var response = controller.GetProducts(storeId);

            // Assert
            Assert.IsFalse(response.IsSuccessStatusCode);
            
        }

        [TestMethod]
        public void ImportProductsDosentExist()
        {
            // Arrange
            var products = new List<Product>();
            var productsId = new int[] { 9, 12 };

            MockComercioLibreClient.Setup(l => l.Products(It.IsAny<int[]>()))
                .Returns(new CLProduct[] { });

            MockProductRepository.Setup(l => l.Add(It.IsAny<Product>()))
                .Callback((Product p) => products.Add(p));
                        
            var controller = GetController();            

            // Act
            controller.ImportProducts(productsId);

            // Assert
            Assert.IsFalse(products.Any());
        }

        [TestMethod]
        public void ImportAnyProducts()
        {
            // Arrange
            var products = new List<Product>();
            MockComercioLibreClient.Setup(l => l.Products(It.IsAny<int[]>()))
                .Returns(new CLProduct[]
                {
                    new CLProduct()
                    {
                        Id = 9,
                        Title = "iPhone 6"
                    },
                    new CLProduct()
                    {
                        Id = 12,
                        Title = "Watch"
                    }
                });
            
            MockProductRepository.Setup(l => l.Add(It.IsAny<Product>()))
                .Callback((Product p )=> products.Add(p));


            var controller = GetController();

            var productsId = new int[] { 9, 12 };

            // Act
            controller.ImportProducts(productsId);

            // Assert
            Assert.AreEqual(productsId[0], products.ElementAt(0).Id);
            Assert.AreEqual(productsId[1], products.ElementAt(1).Id);
        }

        [TestMethod]
        public void ImportProductAlreadyImported()
        {
            // Arrange
            var products = new Dictionary<int,Product>();
            products.Add(9, new Product(){ Id = 9, Title = "iPhone 6 Plus" });
            MockComercioLibreClient.Setup(l => l.Products(It.IsAny<int[]>()))
                .Returns(new CLProduct[]
                {
                    new CLProduct()
                    {
                        Id = 9,
                        Title = "iPhone 6"
                    },
                    new CLProduct()
                    {
                        Id = 12,
                        Title = "Watch"
                    }
                });

            MockProductRepository.Setup(l => l.Add(It.Is<Product>(p => !products.ContainsKey(p.Id))))
                .Callback((Product p) => products.Add(p.Id, p));
            MockProductRepository.Setup(l => l.Add(It.Is<Product>(p => products.ContainsKey(p.Id))))
               .Throws(new ApplicationException("Duplicate Id"));
            MockProductRepository.Setup(l => l.ExistId(It.IsAny<int>()))
                .Returns((int id) => products.ContainsKey(id));


            var controller = GetController();
            var productsId = new int[] { 9, 12 };

            // Act
            controller.ImportProducts(productsId);

            // Assert
            Assert.IsTrue(products.ContainsKey(productsId[0]));
            Assert.IsTrue(products.ContainsKey(productsId[1]));
        }

        [TestMethod]
        public void SuscribeAndUpdateImportedProducts()
        {
            // Arrange
            var products = new Dictionary<int, Product>();
            products.Add(9, 
                new Product()
                {
                    Id = 9,
                    Title = "iPhone 6"
                });
            products.Add(12,
                new Product()
                {
                    Id = 12,
                    Title = "Watch"
                });

            var subscribes = new List<KeyValuePair<int, string>>();

            MockProductRepository.Setup(l => l.GetAll())
                .Returns(products.Values);
            MockProductRepository.Setup(l => l.Update(It.IsAny<int>(), It.IsAny<Product>()))
                .Callback((int id, Product p) => products[id] = p);

            MockComercioLibreClient.Setup(l => l.Products(It.IsAny<IEnumerable<int>>()))
                .Returns(new CLProduct[]
                {
                    new CLProduct()
                    {
                        Id = 9,
                        Title = "iPhone 6 Plus"
                    },
                    new CLProduct()
                    {
                        Id = 12,
                        Title = "Watch"
                    }
                });
            MockComercioLibreClient.Setup(l => l.UpdateProduct(It.IsAny<int>(), It.IsAny<string>()))
                .Callback((int id, string u) => subscribes.Add(new KeyValuePair<int, string>(id, u)));

            var controller = GetController();
            var url = "http://dev.style.com";

            // Act
            controller.SubcribeToImportedProducts(url);

            // Assert
            Assert.AreEqual("iPhone 6 Plus", products[9].Title);
            Assert.AreEqual("Watch", products[12].Title);
            Assert.AreEqual(9, subscribes.ElementAt(0).Key);
            Assert.AreEqual(url, subscribes.ElementAt(0).Value);
            Assert.AreEqual(12, subscribes.ElementAt(1).Key);
            Assert.AreEqual(url, subscribes.ElementAt(1).Value);

        }

        private StoreController GetController()
        {
            var controller = new StoreController(MockStoreRepository.Object, MockProductRepository.Object, MockComercioLibreClient.Object)
            {
                Request = new HttpRequestMessage(),
                Configuration = new HttpConfiguration()
            };
            return controller;
        }

    }
}
